home *** CD-ROM | disk | FTP | other *** search
/ Aminet 1 / Aminet - June 1993 [Walnut Creek].iso / usenet / sources / volume90 / examples / patternc / part01
Encoding:
Internet Message Format  |  1990-04-28  |  13.8 KB

  1. Path: xanth!cs.odu.edu!Amiga-Request
  2. From: Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator)
  3. Newsgroups: comp.sources.amiga
  4. Subject: v90i159: pattern.c - pattern matching routine from disksalv, Part01/01
  5. Message-ID: <12363@xanth.cs.odu.edu>
  6. Date: 28 Apr 90 18:49:25 GMT
  7. Sender: tadguy@cs.odu.edu
  8. Reply-To: daveh@cbmvax.uucp (Dave Haynie)
  9. Lines: 409
  10. Approved: tadguy@cs.odu.edu (Tad Guy)
  11. X-Mail-Submissions-To: Amiga@cs.odu.edu
  12. X-Post-Discussions-To: comp.sys.amiga
  13.  
  14. Submitted-by: daveh@cbmvax.uucp (Dave Haynie)
  15. Posting-number: Volume 90, Issue 159
  16. Archive-name: examples/pattern.c/part01
  17.  
  18. This is the stand-alone version of a pattern matching routine I wrote,
  19. originally for DiskSalv.  It matches the full 1.3 AmigaDOS pattern 
  20. language, not just the #? metacharacters handled by some of the pattern
  21. routines included with the various compilers on the market.  And the 
  22. actual pattern functions, CompilePattern() and MatchPattern(), compile
  23. to under 2k of object code.
  24.  
  25. This code is completely original, and I'm making it public domain to
  26. encourage its use in other programs.  Even commercial programs.  I didn't
  27. research the problem, I just attacked it, so I can't swear there isn't a
  28. more efficient way to handle this stuff.  But it does seem to work.
  29.  
  30. This all compiled under Lattice C, but confuses the global optimizer as
  31. of Lattice V5.02.  It could probably be ported to Manx, or UNIX for
  32. that matter -- it's not real system specific.
  33.  
  34.                     -Dave Haynie
  35.  
  36. #!/bin/sh
  37. # This is a shell archive.  Remove anything before this line, then unpack
  38. # it by saving it into a file and typing "sh file".  To overwrite existing
  39. # files, type "sh file -c".  You can also feed this as standard input via
  40. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  41. # will see the following message at the end:
  42. #        "End of archive 1 (of 1)."
  43. # Contents:  pattern.c
  44. # Wrapped by tadguy@xanth on Sat Apr 28 14:48:53 1990
  45. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  46. if test -f 'pattern.c' -a "${1}" != "-c" ; then 
  47.   echo shar: Will not clobber existing file \"'pattern.c'\"
  48. else
  49. echo shar: Extracting \"'pattern.c'\" \(10896 characters\)
  50. sed "s/^X//" >'pattern.c' <<'END_OF_FILE'
  51. X/* ====================================================================== */
  52. X
  53. X/* 
  54. X            AmigaDOS Pattern Matching Demo
  55. X                March 20, 1990
  56. X
  57. X                by Dave Haynie
  58. X
  59. X            Released to the Public Domain
  60. X
  61. X    This is the AmigaDOS Pattern Matcher I developed for DiskSalv 
  62. X    V1.40.  So far, it seems to work just fine, and certainly works
  63. X    better than any public domain code I managed to dig up, most
  64. X    of which was limited to the '?' and '#' characters instead of
  65. X    full AmigaDOS patterns.
  66. X    
  67. X    This will compile under Lattice V5.02 with the command:
  68. X    
  69. X            lc -L pattern.c
  70. X            
  71. X    The Lattice global optimizer seems to have trouble with it, at
  72. X    least with the V5.02 system.
  73. X
  74. X    With the function prototypes removed, I suspect it would be a
  75. X    simple port to the Manx V3.6a compiler.
  76. X    
  77. X    I'd certainly appreciate any feedback on this code, especially 
  78. X    if there are any patterns it doesn't correctly match.  
  79. X*/
  80. X
  81. X#include <exec/types.h>
  82. X#include <string.h>
  83. X#include <stdio.h>
  84. X#include <setjmp.h>
  85. X
  86. X/* ====================================================================== */
  87. X
  88. X/* These are my allocator routines, slight variations on the C library
  89. X   routines.  The pattern matching code was designed to use the DiskSalv
  90. X   safe chunky allocator, and rather than re-write the code to use
  91. X   something else, I provide some basically equivalent routines below.
  92. X   The pattern compiler allocates string memory, and works best with a
  93. X   memory allocator that can be cleaned up at program's end (eg, a plain
  94. X   AllocMem() here isn't a good idea). */
  95. X
  96. Xjmp_buf        lowmem;        /* Don't forget to initialize this! */
  97. X
  98. X/* This is the basic safe allocator; it always returns a valid pointer.
  99. X   The pattern matcher counts on the memory returned having been zeroed
  100. X   out! */
  101. X
  102. Xchar *salloc(LONG size) {
  103. X   char *ptr;
  104. X   
  105. X   if (!(ptr = (char *)malloc(size))) longjmp(lowmem,-1);
  106. X   setmem(ptr,size,'\0');
  107. X   return ptr;    
  108. X}
  109. X
  110. X/* This is just for compatibility purposes. */
  111. X
  112. Xvoid sfree(char *ptr) {
  113. X   free(ptr);
  114. X}
  115. X
  116. X/* ====================================================================== */
  117. X
  118. X/* This is the definition of a pattern element.  The pattern compiler will
  119. X   create an array of these elements from a string passed to it. */
  120. X
  121. Xtypedef struct {
  122. X   char *aux;            /* Auxilary data; string or paren-count */
  123. X   BYTE type;            /* Corresponding character type */
  124. X} pattern;
  125. X
  126. X/* The types, for the "special" array. */
  127. X
  128. X#define pSTRING    0    /* Plain old string */
  129. X#define pALT    1    /* The alternation character */
  130. X#define pREP    2    /* The repeat character */
  131. X#define pANY    3    /* The match-any character */
  132. X#define pNULL    4    /* The match-null character */
  133. X#define pSUB    5    /* A subpattern */
  134. X#define pEND    6    /* End of the pattern. */
  135. X
  136. X/* This is the definition of a pattern matching function, for use in the
  137. X   "MatchSub()" coroutine. */
  138. X
  139. Xtypedef BOOL (*patfunc)(pattern *, char *);
  140. X
  141. X/* ====================================================================== */
  142. X
  143. X/* This section contains the pattern compiler code. */
  144. X
  145. X/* This function takes in a string from which one parenthesis has been
  146. X   stripped.  It returns a pointer to the position of the matching
  147. X   parenthesis, or NULL if it isn't found. */
  148. X
  149. Xstatic char *FindParen(char *str) {
  150. X   short parencnt = 1;
  151. X   
  152. X   while (*str) {
  153. X      switch (*str) {
  154. X         case '(' :
  155. X            ++parencnt;
  156. X            break;
  157. X         case ')' :
  158. X            if (--parencnt == 0) return str;
  159. X            break;
  160. X         default :
  161. X            break;          
  162. X      }
  163. X      ++str;
  164. X   }
  165. X   return NULL;
  166. X}
  167. X
  168. X/* This function creates a pattern for later pattern matching.  It checks
  169. X   syntax as well, since we don't want invalid patterns to possibly crash 
  170. X   the machine or otherwise cause trouble.  This is a destructive compile,
  171. X   in that it trashes the input string. */
  172. X        
  173. Xpattern *CompilePattern(char *str) {
  174. X   pattern *pat, *result = NULL;
  175. X   short i = 0, j, tlen, plen = strlen(str)+1;
  176. X   char  fch, nch, *sub, *tmp;
  177. X
  178. X   if (!str) return NULL;
  179. X   
  180. X   pat = (pattern *)salloc(sizeof(pattern)*plen);
  181. X   while (fch = *str++) {
  182. X      nch = *str;
  183. X      switch (fch) {
  184. X         case '(' :    /* The start of a group */
  185. X            if (nch == '|' || nch == ')') goto fail;
  186. X            pat[i].type = pSUB;
  187. X            if (!(str = FindParen(sub = str))) goto fail;
  188. X            *str++ = '\0';
  189. X            if (!(pat[i].aux = (char *)CompilePattern(sub))) goto fail;
  190. X            break;
  191. X         case ')' :    /* We should never see the end of a group */
  192. X            goto fail;
  193. X         case '|' :    /* Alternation */
  194. X            if (nch == '|' || nch == ')') goto fail;
  195. X            pat[i].type = pALT;
  196. X            break;
  197. X         case '#' :    /* Repeatition */
  198. X        if (nch == '#' || nch == '|' || nch == ')' || !nch) goto fail;
  199. X        pat[i].type = pREP;
  200. X        switch (nch) {
  201. X           case '(' :
  202. X           case '%' :
  203. X           case '?' :
  204. X              break;
  205. X           case '\047':
  206. X              nch = *str++;
  207. X           default:
  208. X                  pat[++i].type = pSTRING;
  209. X                  pat[i].aux = (char *)salloc(2);
  210. X                  pat[i].aux[0] = nch;
  211. X              str++;
  212. X              break;
  213. X            }
  214. X            break;
  215. X         case '?' :    /* One of anything */
  216. X            pat[i].type = pANY;
  217. X            break;
  218. X         case '%' :    /* One of nothing */
  219. X            pat[i].type = pNULL;
  220. X            break;
  221. X         default:    /* Normal characters */
  222. X            pat[i].type = pSTRING;
  223. X            tmp = (char *)salloc((long)(tlen = strlen(str)+2));
  224. X            tmp[0] = fch;
  225. X            j = 1;
  226. X            while (*str && !strchr("()|#?%",*str)) {
  227. X               if (*str == '\047') ++str;
  228. X               tmp[j++] = *str++;
  229. X            }
  230. X            tmp[j] = '\0';
  231. X            strupr(strcpy(pat[i].aux = salloc(j+1),tmp));
  232. X            sfree(tmp);
  233. X            break;
  234. X      }
  235. X      ++i;
  236. X   }
  237. X   pat[i++].type = pEND;
  238. X   result = (pattern *)memcpy(salloc(sizeof(pattern)*i),(char *)pat,sizeof(pattern)*i);
  239. X
  240. Xfail:
  241. X   sfree((char *)pat);
  242. X   return result;
  243. X}
  244. X
  245. X/* ====================================================================== */
  246. X
  247. X/* This is the actual pattern matching code.  This code hasn't been really
  248. X   optimized or anything; it's highly recursive, but it's designed to be
  249. X   as nice to your stack as possible.  There are four coroutines in my 
  250. X   pattern matcher.  The top-level routine "MatchPattern()" takes in a 
  251. X   pattern and a string, and knows only how to subdivide an alternated 
  252. X   pattern and pass that on the the single pattern matcher, "MatchOne()".  
  253. X   That routine knows how to match strings, NULLs, and ANYs.  It calls 
  254. X   "MatchRepeat()" to handle the repeat pattern, and "MatchSub()" to handle
  255. X   a sub-pattern.  Similarly, the "MatchRepeat()" routine knows how to match
  256. X   repeated strings, NULLs, and ANYs.  It also calls the "MatchSub()" routine
  257. X   to handle repeated subpatterns.  The "MatchSub()" splits the input string
  258. X   into strings for the subpattern and parent pattern to match, and works 
  259. X   through these until a match is found or we've tried all possibilities.  
  260. X   It calls "MatchPattern()" on a simple subpattern, or "MatchRepeat()" on a 
  261. X   repeated subpattern. */
  262. X
  263. X/* Forward Declarations */
  264. X
  265. Xstatic BOOL MatchOne(pattern *, char *);
  266. Xstatic BOOL MatchSub(pattern *, pattern *, char *, patfunc);
  267. Xstatic BOOL MatchRepeat(pattern *, char *);
  268. X       BOOL MatchPattern(pattern *, char *);
  269. X
  270. X/* This function matches a subpattern, where "sub" is the subpattern,
  271. X   "pat" is the rest of the pattern at the current level, and "str"
  272. X   is the usual string.  The string splitting uses dynamic allocation
  273. X   to avoid stack overflows as much as possible, the "subob" function
  274. X   is the particular pattern matching function to use on the subpattern. */
  275. X
  276. Xstatic BOOL MatchSub(pattern *sub, pattern *pat, char *str, patfunc subop) {
  277. X   short pos, len = strlen(str)+1;
  278. X   char *copy = salloc(strlen(str)+1);
  279. X
  280. X   for (pos = 0; pos < len; pos++) {
  281. X      if ((*subop)(sub,copy) && MatchOne(pat,str)) {
  282. X         sfree(copy);
  283. X         return TRUE;
  284. X      }
  285. X      copy[pos] = *str++;
  286. X   }
  287. X   sfree(copy);
  288. X   return FALSE;
  289. X}
  290. X
  291. X/* This function matches a repeat pattern.  It returns the part of the
  292. X   string not matched, or NULL if the whole string was matched. */
  293. X
  294. Xstatic BOOL MatchRepeat(pattern *pat, char *str) {
  295. X   pattern *local = pat++;
  296. X
  297. X   switch (local->type) {
  298. X      case pSTRING    :
  299. X         while (TRUE) {
  300. X            if (MatchPattern(pat,str)) return TRUE;
  301. X            if (strnicmp(str,local->aux,strlen(local->aux))) break;
  302. X            str += strlen(local->aux);
  303. X         }
  304. X         break;
  305. X      case pANY    :
  306. X         do {
  307. X            if (MatchPattern(pat,str)) return TRUE;
  308. X         } while (*++str);
  309. X         if (pat[0].type == pEND || pat[0].type == pALT) return TRUE;
  310. X         break;
  311. X      case pNULL    :
  312. X         break;
  313. X      case pSUB    :
  314. X         if (MatchPattern(pat,str)) return TRUE;
  315. X         return MatchSub((pattern *)local->aux,pat,str,MatchRepeat);
  316. X      case pEND    :
  317. X         break;
  318. X   }
  319. X   return MatchOne(pat,str);
  320. X}
  321. X
  322. X/* This function tries to match a single pattern against the given 
  323. X   string.  It is up to the calling function to worry about alternation. */
  324. X
  325. Xstatic BOOL MatchOne(pattern *pat, char *str) {
  326. X   if (!str) return FALSE;
  327. X
  328. X   while (pat->type != pEND && pat->type != pALT) {
  329. X      switch (pat->type) {
  330. X         case pSTRING:
  331. X            if (strnicmp(str,pat->aux,strlen(pat->aux))) return FALSE;
  332. X            str += strlen(pat->aux);
  333. X            break;
  334. X         case pREP    :
  335. X            return MatchRepeat(++pat,str);
  336. X         case pANY    :         
  337. X            if (!*str++) return FALSE;
  338. X            break;
  339. X         case pNULL    :
  340. X            break;
  341. X         case pSUB    :
  342. X            return MatchSub((pattern *)pat->aux,pat+1,str,MatchPattern);
  343. X      }
  344. X      ++pat;
  345. X   }
  346. X   if (!*str) return TRUE;
  347. X   return FALSE;
  348. X}
  349. X
  350. X/* This function matches a given string against a precompiled pattern.  The
  351. X   main function here doesn't do any actual matching; it just subdivides
  352. X   a pattern into individual items based on any alternation used. */
  353. X
  354. XBOOL MatchPattern(pattern *pat, char *str) {
  355. X   if (!str) return FALSE;
  356. X
  357. X   while (pat && pat->type != pEND) {
  358. X      if (MatchOne(pat,str)) return TRUE;
  359. X
  360. X      while (pat->type != pALT && pat->type != pEND) ++pat;
  361. X      if (pat->type == pALT) ++pat;
  362. X   } 
  363. X   return FALSE;
  364. X}
  365. X
  366. X/* ====================================================================== */
  367. X
  368. X/* Here's a simple main program to use the pattern matcher. */
  369. X
  370. Xvoid main(int argc, char *argv[]) {
  371. X   pattern *pat;
  372. X   int i;
  373. X
  374. X   if (setjmp(lowmem)) {
  375. X      printf("Error: Out of memory!\n");
  376. X      exit(10);
  377. X   }
  378. X   if (argc < 2) {
  379. X      printf("Usage: %s pattern str1 [str2 ... strN]\n",argv[0]);
  380. X      exit(0);
  381. X   }
  382. X   if (!(pat = CompilePattern(argv[1]))) {
  383. X      printf("Error: Invalid pattern!\n");
  384. X      exit(10);
  385. X   }
  386. X
  387. X   for (i = 2; i < argc; ++i) {
  388. X      if (MatchPattern(pat,argv[i]))
  389. X         printf("MATCH  ");
  390. X      else
  391. X         printf("NOMATCH");
  392. X
  393. X      printf(": \"%s\"\n",argv[i]);
  394. X   }
  395. X}
  396. END_OF_FILE
  397. if test 10896 -ne `wc -c <'pattern.c'`; then
  398.     echo shar: \"'pattern.c'\" unpacked with wrong size!
  399. fi
  400. # end of 'pattern.c'
  401. fi
  402. echo shar: End of archive 1 \(of 1\).
  403. cp /dev/null ark1isdone
  404. MISSING=""
  405. for I in 1 ; do
  406.     if test ! -f ark${I}isdone ; then
  407.     MISSING="${MISSING} ${I}"
  408.     fi
  409. done
  410. if test "${MISSING}" = "" ; then
  411.     echo You have the archive.
  412.     rm -f ark[1-9]isdone
  413. else
  414.     echo You still need to unpack the following archives:
  415.     echo "        " ${MISSING}
  416. fi
  417. ##  End of shell archive.
  418. exit 0
  419. -- 
  420. Mail submissions (sources or binaries) to <amiga@cs.odu.edu>.
  421. Mail comments to the moderator at <amiga-request@cs.odu.edu>.
  422. Post requests for sources, and general discussion to comp.sys.amiga.
  423.